package com.qianfeng.smartplatform.config;

//
//                            _ooOoo_  
//                           o8888888o  
//                           88" . "88  
//                           (| -_- |)  
//                            O\ = /O  
//                        ____/`---'\____  
//                      .   ' \\| |// `.  
//                       / \\||| : |||// \  
//                     / _||||| -:- |||||- \  
//                       | | \\\ - /// | |  
//                     | \_| ''\---/'' | |  
//                      \ .-\__ `-` ___/-. /  
//                   ___`. .' /--.--\ `. . __  
//                ."" '< `.___\_<|>_/___.' >'"".  
//               | | : `- \`.;`\ _ /`;.`/ - ` : | |  
//                 \ \ `-. \_ __\ /__ _/ .-` / /  
//         ======`-.____`-.___\_____/___.-`____.-'======  
//                            `=---='  
//  
//         .............................................  
//                  佛祖镇楼                  BUG辟易  
//          佛曰:  
//                  写字楼里写字间，写字间里程序员；  
//                  程序人员写程序，又拿程序换酒钱。  
//                  酒醒只在网上坐，酒醉还来网下眠；  
//                  酒醉酒醒日复日，网上网下年复年。  
//                  但愿老死电脑间，不愿鞠躬老板前；  
//                  奔驰宝马贵者趣，公交自行程序员。  
//                  别人笑我忒疯癫，我笑自己命太贱；  
//  


import com.qianfeng.smartplatform.cache.MenuCache;
import com.qianfeng.smartplatform.pojo.Menu;
import com.qianfeng.smartplatform.security.handlers.MyUrlAuthenticationFailureHandler;
import com.qianfeng.smartplatform.security.handlers.MyUrlAuthenticationSuccessHandler;
import com.qianfeng.smartplatform.security.service.MyUserDetailService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;

import java.util.List;

/**
 * Created by Jackiechan on 2022/6/6 9:21
 *
 * @author Jackiechan
 * @version 1.0
 * @since 1.0
 */
@Configuration
@EnableWebSecurity//开启spring security
//启用注解模式配置权限
@EnableGlobalMethodSecurity(prePostEnabled = true, securedEnabled = true, proxyTargetClass = true)
public class MyWebSecurityConfig extends WebSecurityConfigurerAdapter {

    private MyUserDetailService myUserDetailService;

    private BCryptPasswordEncoder bCryptPasswordEncoder;

    private MyUrlAuthenticationSuccessHandler myUrlAuthenticationSuccessHandler;

    private MyUrlAuthenticationFailureHandler myUrlAuthenticationFailureHandler;
    //菜单的缓存数据,里面存放了每个对应的地址和需要的权限
    private MenuCache menuCache;


    @Autowired
    public void setMenuCache(MenuCache menuCache) {
        this.menuCache = menuCache;
    }

    @Autowired
    public void setMyUrlAuthenticationFailureHandler(MyUrlAuthenticationFailureHandler myUrlAuthenticationFailureHandler) {
        this.myUrlAuthenticationFailureHandler = myUrlAuthenticationFailureHandler;
    }

    @Autowired
    public void setMyUrlAuthenticationSuccessHandler(MyUrlAuthenticationSuccessHandler myUrlAuthenticationSuccessHandler) {
        this.myUrlAuthenticationSuccessHandler = myUrlAuthenticationSuccessHandler;
    }

    @Autowired
    public void setbCryptPasswordEncoder(BCryptPasswordEncoder bCryptPasswordEncoder) {
        this.bCryptPasswordEncoder = bCryptPasswordEncoder;
    }

    @Autowired
    public void setMyUserDetailService(MyUserDetailService myUserDetailService) {
        this.myUserDetailService = myUserDetailService;
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        //配置我们项目中什么地址需要什么权限,spring security会自动帮我们判断
        http.csrf().disable();//跨域先禁止,不然项目中很多地址都不能访问
        //配置地址需要什么权限,获取到所有的地址
        List<Menu> allMenus = menuCache.getAllData();
        //必须需要权限的才设置
        allMenus.stream().filter(menu -> !"#".equals(menu.getUrl()) && menu.getUrl() != null && !menu.getUrl().equalsIgnoreCase("")).forEach(menu -> {
            try {
                //设置每个地址需要的权限
                http.authorizeRequests().antMatchers(menu.getUrl()).hasAuthority(menu.getPerms());
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });

        http.authorizeRequests()//认证所有的请求
                .and().formLogin()
                //让spring security拦截我们的登录操作,注意这个地址在我们的项目中不要存在,
                // spring security会自动创建这个地址,我们不需要写判断逻辑,
                // 我们只需要告诉spring security用户数据库中的密码是什么,spring security会自动帮我们进行逻辑判断
                //那怎么知道是哪个用户的密码,会有专门的一个类来进行处理
                .loginProcessingUrl("/login")
                //当spring security发现登录成功后跳转到什么地址
                //  .successForwardUrl("/dht11.html")
                //设置自定义的登录成功之后的处理器
                .successHandler(myUrlAuthenticationSuccessHandler)
                //当登录失败的时候跳转到什么地址,一般都是密码错误,应该重新登录,默认就是在登录页面
                // .failureForwardUrl()
                .failureHandler(myUrlAuthenticationFailureHandler)
                //上面的地址需要放行,我们知道登录地址是需要放行的
                .permitAll()
                //配置退出的
                .and().logout()
                //和登录地址一样,不需要我们写这个地址,spring security会自动帮我们处理退出
                .logoutUrl("/logout")
                //退出后跳转到登录地址
                .logoutSuccessUrl("/login")
                //退出地址需要放行
                .permitAll()
                //其他的没有配置的地址都需要登录才可以访问
                .and().authorizeRequests().anyRequest().authenticated();

    }


    /**
     * 忽略的地址, 在这里配置的地址不会经过spring security,这里面一般忽略的是js css等资源文件,这种本身连登录都不需要的地址就放到这里面来
     * 和permitAll() 不一样
     *
     * @param web
     * @throws Exception
     */
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/js/**",
                "/layui/**",
                "/websocket/*",
                "/colorcommand/*",
                "/humiture/*"
        );
    }

    /**
     * 告诉spring security 当前登录的用户具体怎么获取到密码以及密码是怎么编码的
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(myUserDetailService).passwordEncoder(bCryptPasswordEncoder);
    }
}
